package org.mysterylab.utopiaframework.commons.compass.command;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.compass.core.CompassQuery.SortPropertyType;
import org.compass.core.support.search.CompassSearchCommand;
import org.mysterylab.utopiaframework.commons.compass.controller.AdvancedSearchController;

/**
 * 高级搜索的命令. 主要增加了如下功能(可参考 Google 的高级检索功能:
 * <a href="http://www.google.com.hk/advanced_search?hl=zh-CN">Google高级检索</a>):
 * <ul>
 * 	<li>1) 包含全部单词.</li>
 * 	<li>2) 包含任意单词.</li>
 * 	<li>3) 不包含如下单词.</li>
 * 	<li>4) 每页显示的记录数.</li>
 * 	<li>5) 分类条件(比如图书中的分 Category 来进行检索).</li>
 * </ul><br>
 * 另外, 还可以继续扩展其他的搜索需求. 比如, 我们可以在该高级检索的基础上增加分类的检索, 如:
 * <pre>
 *  public class BookSearchCommand extends AdvancedSearchCommand {
 *  
 *      // 增加图书分类的条件
 *      private String categoryName;
 *      
 *      public String getCategoryName() {
 *          return categoryName;
 *      }
 *      
 *      public void setCategoryName(String categoryName) {
 *          this.categoryName = categoryName;
 *      }
 *  }
 * </pre>
 * 
 * @author zhouych
 * @see CompassSort
 * @see AdvancedSearchController
 */
public class AdvancedSearchCommand extends CompassSearchCommand {

	/**
	 * 包含全部单词
	 */
	private String full;

	/**
	 * 包含任意单词
	 */
	private String anyone;

	/**
	 * 不包含以下的单词
	 */
	private String exclude;

	/**
	 * 每条显示的记录数
	 */
	private Integer pageSize;

	/**
	 * 搜索条件页所用的数据, 如图书分类
	 */
	@SuppressWarnings("unchecked")
	private Map referenceData = new HashMap();

	/**
	 * 搜索结果排序表
	 */
	private Set<CompassSort> sortMap = new HashSet<CompassSort>();

	public String getAnyone() {
		return anyone;
	}

	public void setAnyone(String anyone) {
		this.anyone = anyone;
	}

	public String getFull() {
		return full;
	}

	public void setFull(String full) {
		this.full = full;
	}

	public String getExclude() {
		return exclude;
	}

	public void setExclude(String exclude) {
		this.exclude = exclude;
	}

	public Integer getPageSize() {
		return pageSize;
	}

	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
	}

	@SuppressWarnings("unchecked")
	public Map getReferenceData() {
		return referenceData;
	}

	@SuppressWarnings("unchecked")
	public void setReferenceData(Map referenceData) {
		this.referenceData = referenceData;
	}

	public Set<CompassSort> getSortMap() {
		return sortMap;
	}

	public void setSortMap(Set<CompassSort> sortMap) {
		this.sortMap = sortMap;
	}

	/**
	 * 给该搜索命令增加排序.
	 * 
	 * @param sortParamName
	 *            增加的排序名
	 * @param paramType
	 *            增加的排序类型, 现主要定义了三种类型: int、float 和 string. 其他的都被定义为 {@link SortPropertyType#AUTO}
	 * @param isAscend
	 *            顺序还是倒序排序
	 */
	public void addSort(String sortParamName, String paramType, boolean isAscend) {
		this.sortMap.add(new CompassSort(sortParamName, paramType, isAscend));
	}

	/**
	 * 在设置完成各种参数后, 必须调用此方法, 完成对这些参数进行 Lucene 的查询语句的构建.
	 */
	public void setupQuery() {
		StringBuffer sb = new StringBuffer();
		
		// 包含所有单词
		if (StringUtils.isNotBlank(this.getFull())) {
			Object[] texts = splitQuery(this.getFull());
			for (int i = 0; i < texts.length; i++) {
				if (StringUtils.isEmpty((String) texts[i])) {
					continue;
				}
				sb.append(texts[i]);
				if (i != texts.length - 1) {
					sb.append(" AND ");
				}
			}
		}
		
		// 包含任意单词
		if (StringUtils.isNotBlank(this.getAnyone())) {
			sb.append(" ");
			sb.append(this.getAnyone());
			sb.append(" ");
		}
		
		// 不包含以下单词
		if (StringUtils.isNotBlank(this.getExclude())) {
			Object[] texts = splitQuery(this.getExclude());
			for (Object text : texts) {
				if (StringUtils.isEmpty((String) text)) {
					continue;
				}
				sb.append(" -");
				sb.append(text);
			}
		}
		// 最后调用子类的扩展点
		onAfterCommandSetup(sb);

		if (StringUtils.isNotEmpty(sb.toString())) {
			this.setQuery(sb.toString());
		}
		if (this.getPageSize() != null) {
			this.setPageSize(this.getPageSize());
		}
		// 调用子类函数, 将 Category 分类这样的对象放入 command 中供查询条件页使用
		referenceData();
	}

	/**
	 * 分割查询字符串成数组, 保留""字符. 比如:<br>
	 * "aa bb" cc => String[]{"aa bb",cc} <br>
	 * aa bb cc => String[]{aa, bb,cc}
	 * 
	 * @param query
	 *            进行分割的查询语句
	 * @return
	 */
	protected String[] splitQuery(String query) {
		
		List<String> list = new ArrayList<String>();
		Pattern pattern = Pattern
				.compile("\\s*(\\u0022\\s*\\S+.*\\S+\\s*\\u0022)|(\\S+)\\s*");

		Matcher matcher = pattern.matcher(query);

		while (matcher.find()) {
			String key = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
			list.add(key);
		}
		return list.toArray(new String[0]);
	}

	protected void onAfterCommandSetup(StringBuffer sb) {
	}

	protected void referenceData() {
	}
}